#include "stdafx.h"
#include "Utils.h"

#include <sys/types.h>
#include <sys/stat.h>

#include <shellapi.h>
#include <winerror.h>

#include <algorithm>

const TCHAR cPathDelimiter = _T('\\');
const TCHAR cExtDelimiter = _T('.');

bool GetTempPath(tstring& sTempPath)
{
	DWORD dwBufSize = GetTempPath(0,NULL);
	
	if( dwBufSize == 0 )
		return false;
	
	LPTSTR TempPath = new TCHAR[dwBufSize];
	ZeroMemory(TempPath, sizeof(TCHAR)*dwBufSize );

	bool bResult = GetTempPath(dwBufSize, TempPath) != 0;
	
	if( bResult )
		sTempPath = TempPath;

	delete[] TempPath;
	return bResult;
}

bool GetTempFileName(tstring& sTempFileName)
{
	tstring TempDir;
	if( !GetTempPath(TempDir) )
		return false;
	
	TCHAR TempFileName[MAX_PATH];
	ZeroMemory(TempFileName, sizeof(TCHAR)*MAX_PATH);

	bool bResult = GetTempFileName(TempDir.c_str(), NULL, 0, TempFileName) != 0;
	if( bResult )
		sTempFileName = TempFileName;
	return bResult;
}

#pragma warning(push)
#pragma warning(disable: 4996)
void ExtractFileExt(const tstring& sFilePath, tstring& sExt)
{
	TCHAR FileExt[_MAX_EXT];
	ZeroMemory(FileExt, sizeof(TCHAR)*_MAX_EXT);
	_tsplitpath(sFilePath.c_str(), NULL, NULL, NULL, FileExt);
	sExt = FileExt;
}

void ExtractFileName_WithPath_WithoutExt(const tstring& InPath, tstring& sFileName)
{
	// copypaste. refactor it!!!
	TCHAR FileName[_MAX_FNAME];
	TCHAR DirPath[_MAX_DIR];
	TCHAR DriveLetter[_MAX_DRIVE];
	ZeroMemory(FileName, sizeof(TCHAR)*_MAX_FNAME );
	ZeroMemory(DirPath, sizeof(TCHAR)*_MAX_DIR);
	ZeroMemory(DriveLetter, sizeof(TCHAR)*_MAX_DRIVE);
	
	_tsplitpath(InPath.c_str(), DriveLetter, DirPath, FileName, NULL);

	TCHAR NewFilePath[_MAX_PATH];
	ZeroMemory(NewFilePath, sizeof(TCHAR)*_MAX_PATH);

	_tmakepath(NewFilePath, DriveLetter, DirPath, FileName, NULL);

	sFileName = NewFilePath;
}

void ExtractFileName_WithoutPath_WithExt(const tstring& InPath, tstring& sFileName)
{
	// copypaste. refactor it!!!
	TCHAR FileName[_MAX_FNAME];
	TCHAR FileExt[_MAX_EXT];
	ZeroMemory(FileName, sizeof(TCHAR)*_MAX_FNAME );
	ZeroMemory(FileExt, sizeof(TCHAR)*_MAX_EXT);
	
	_tsplitpath(InPath.c_str(), NULL, NULL, FileName, FileExt);

	TCHAR NewFilePath[_MAX_PATH];
	ZeroMemory(NewFilePath, sizeof(TCHAR)*_MAX_PATH);

	_tmakepath(NewFilePath, NULL, NULL, FileName, FileExt);

	sFileName = NewFilePath;
}

void ExtractFileName_WithoutPath_WithoutExt(const tstring& InPath, tstring& sFileName)
{
	// copypaste. refactor it!!!
	TCHAR FileName[_MAX_FNAME];
	ZeroMemory(FileName, sizeof(TCHAR)*_MAX_FNAME );
	
	_tsplitpath(InPath.c_str(), NULL, NULL, FileName, NULL);

	sFileName = FileName;
}

void ChangeFileExt(const tstring& sFilePath, const tstring& sNewExt, /*out*/ tstring& sNewFilePath, bool ReplaceExt)
{
	// copypaste. refactor it!!!
	TCHAR FileName[_MAX_FNAME];
	TCHAR DirPath[_MAX_DIR];
	TCHAR DriveLetter[_MAX_DRIVE];
	TCHAR FileExt[_MAX_EXT];
	ZeroMemory(FileName, sizeof(TCHAR)*_MAX_FNAME );
	ZeroMemory(DirPath, sizeof(TCHAR)*_MAX_DIR);
	ZeroMemory(DriveLetter, sizeof(TCHAR)*_MAX_DRIVE);
	ZeroMemory(FileExt, sizeof(TCHAR)*_MAX_EXT);
	
	_tsplitpath(sFilePath.c_str(), DriveLetter, DirPath, FileName, FileExt);
	
	TCHAR NewFilePath[_MAX_PATH];
	ZeroMemory(NewFilePath, sizeof(TCHAR)*_MAX_PATH);
	
	tstring sActualNewExt;
	if( ReplaceExt )
	{
		sActualNewExt = sNewExt;
	}
	else
	{
		sActualNewExt = FileExt;
		if( (sNewExt.begin() != sNewExt.end()) && (*sNewExt.begin() != cExtDelimiter ) )
		{
			sActualNewExt += cExtDelimiter;
		}
		sActualNewExt += sNewExt;
	}
	_tmakepath(NewFilePath, DriveLetter, DirPath, FileName, sActualNewExt.c_str());
	
	sNewFilePath = NewFilePath;
}

bool ChangeFileExtAndRename(const tstring& FilePath, const tstring& NewExt, tstring& sNewFilePath, bool ReplaceExt)
{
	ChangeFileExt( FilePath, NewExt, sNewFilePath, ReplaceExt);
	return _trename(FilePath.c_str(), sNewFilePath.c_str() ) == 0; 
}
#pragma warning(pop)

bool PathContainsWildcards(const tstring& Path)
{
	return (std::find(Path.begin(), Path.end(), _T('*') ) != Path.end()) ||
		(std::find(Path.begin(), Path.end(), _T('?') ) != Path.end() );
}

bool LPCTSTR2stdstr(LPCTSTR lpInStr, std::string& lpOutStr)
{
	_ASSERT( lpInStr != NULL );
#ifdef UNICODE
	return USC_2ToOEMstdstr(lpInStr, lpOutStr);
#else
	lpOutStr = lpInStr;
	return true;
#endif
}

bool stdstr2stdwstr(const std::string& InStr, std::wstring& OutStr)
{
	LPWSTR USC_2str = NULL;

	int nBytesNeeded = MultiByteToWideChar( CP_ACP, 0, InStr.c_str(), -1, USC_2str, 0 );
	if (nBytesNeeded == 0 )
		return false;
	USC_2str = new wchar_t[nBytesNeeded];
	bool bResult = MultiByteToWideChar( CP_ACP, 0, InStr.c_str(), -1, USC_2str, nBytesNeeded) != 0;
	if( bResult )
		OutStr = USC_2str;
	delete[] USC_2str;
	return bResult;
}

bool USC_2ToOEMstdstr(LPCWSTR usc_2str, std::string& OutputOEMstr)
{
	_ASSERT( usc_2str != NULL );

	LPSTR OEMstr = NULL;

	int nBytesNeeded = WideCharToMultiByte( CP_ACP, 0, usc_2str, -1, OEMstr, 0, NULL, NULL);
	if (nBytesNeeded == 0)
		return false;
	OEMstr = new char[nBytesNeeded];
	ZeroMemory( OEMstr, sizeof(char)*nBytesNeeded );

	bool bResult = WideCharToMultiByte( CP_ACP, 0, usc_2str, -1, OEMstr, nBytesNeeded, NULL, NULL) != 0;
	if (bResult )
		OutputOEMstr = OEMstr;
	
	delete[] OEMstr;
	return bResult;
}

bool FileExists(const tstring& PathToFile)
{
	struct _stat buf;
	return _tstat( PathToFile.c_str(), &buf ) == 0;
}

void ExcludeTrailingPathDelimiter(const tstring& InPath, tstring& OutPath)
{
	if( InPath.empty() )
		return ;
	tstring::const_iterator LastCharIt = --InPath.end();
	if( *LastCharIt == cPathDelimiter)
	{
		OutPath.resize( InPath.size() - 1);
		copy( InPath.begin() , LastCharIt, OutPath.begin() );
	}
	else
	{
		OutPath.resize( InPath.size() );
		copy( InPath.begin(), InPath.end(), OutPath.begin() );
	}
}

void IncludeTrailingPathDelimiter(const tstring& InPath, tstring& OutPath)
{
	if( InPath.empty() )
	{
		OutPath = cPathDelimiter;
	}
	else
	{
		OutPath.assign(InPath);

		tstring::const_iterator LastCharIt = --InPath.end();
		if( *LastCharIt != cPathDelimiter)
		{
			OutPath += cPathDelimiter;	
		}
	}
}

bool CheckFileExtension(const tstring& PathToFile, const LPCTSTR AllowedExtensions[], size_t AllowedExtensionsCount )
{
	TCHAR FileExt[_MAX_EXT];
	ZeroMemory( FileExt, sizeof(TCHAR)*_MAX_EXT );
#pragma warning(push)
#pragma warning(disable: 4996)
	_tsplitpath( PathToFile.c_str() , NULL, NULL, NULL, FileExt);
#pragma warning(pop)
	
	for( size_t i = 0; i < AllowedExtensionsCount; ++i )
	{
		size_t FileExtLen = _tcslen( FileExt );
		if( (FileExtLen-1 == _tcslen(AllowedExtensions[i]) ) && (_tcsnicmp( (FileExt + 1), AllowedExtensions[i], FileExtLen) == 0) )
			return true;
	}
	return false; 
}

void ExtractFilePath(const tstring& InPath, tstring& OutPath)
{
	tstring::const_reverse_iterator LastDelimiter = find(InPath.rbegin() , InPath.rend(), cPathDelimiter);
	if( LastDelimiter != InPath.rend() )
		LastDelimiter++;
	OutPath.resize( InPath.rend() - LastDelimiter );
	copy(LastDelimiter, InPath.rend(), OutPath.rbegin() );
}

bool CreateDirectory(const tstring& Path)
{
	BOOL bResult = ::CreateDirectory(Path.c_str(), NULL);
	if( bResult != 0 )
		return true;

	DWORD LastError = GetLastError();
	return (LastError == ERROR_ALREADY_EXISTS);
}

bool ForceDirectories(const tstring& Path) // just port of Delphi RTL function
{
	_ASSERT( !Path.empty() );

	tstring Dir;
	ExcludeTrailingPathDelimiter(Path, Dir);
	
	tstring FilePath;
	ExtractFilePath(Dir, FilePath);

	if( (Dir.length() < 3 ) || FileExists(Dir) || (FilePath == Dir) )
		return true;
	
	return ForceDirectories(FilePath) &&
		CreateDirectory(Dir);	
}

bool IsDirectory(const tstring& Path)
{
	struct _stat buf;
	if( _tstat( Path.c_str(), &buf ) != 0 )
		return false;
	return (buf.st_mode & _S_IFDIR) != 0;
}

bool IsDirectoryName(const tstring& Path)
{
	tstring PathWithoutDelim;
	ExcludeTrailingPathDelimiter(Path, PathWithoutDelim);
	return (Path != PathWithoutDelim);
}

void DeleteDirectory(const tstring& Path)
{
	SHFILEOPSTRUCT ShOptions = {0};
	ShOptions.hwnd = 0;
	ShOptions.wFunc = FO_DELETE;

	LPTSTR DubleNullPath = new TCHAR[Path.length() + 3];
	ZeroMemory(DubleNullPath, sizeof(TCHAR)*(Path.length() + 3) );
#pragma warning(push)
#pragma warning(disable: 4996)
	_tcscpy( DubleNullPath, Path.c_str() );
#pragma warning(pop)
	ShOptions.pFrom = DubleNullPath;
	ShOptions.pTo = NULL;
	ShOptions.fFlags = FOF_NOCONFIRMATION | FOF_NOERRORUI | FOF_SILENT;
	
	int Result = SHFileOperation(&ShOptions);
	delete[] DubleNullPath;
}

void GetFullPath(const tstring& RelativePath, tstring& sFullPath)
{
	TCHAR FullPath[_MAX_PATH];
	ZeroMemory(FullPath, sizeof(TCHAR)*_MAX_PATH);
	
	_tfullpath( FullPath, RelativePath.c_str(), _MAX_PATH);
	sFullPath = FullPath;
}

void GetMatchedPaths(const tstring& wildcPath, std::vector<tstring>& MatchedPaths)
{
	_ASSERT( !wildcPath.empty()  );
	
	if( IsDirectoryName(wildcPath) )
		return ; // No directories.

	tstring BasePath;
	ExtractFilePath( wildcPath, BasePath);
	
	GetFullPath(BasePath, BasePath);
	IncludeTrailingPathDelimiter(BasePath, BasePath);

	WIN32_FIND_DATA FindData = {0};
	HANDLE hSearch = ::FindFirstFile(wildcPath.c_str(), &FindData);
	if( hSearch != INVALID_HANDLE_VALUE )
	{
		if( (FindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0 ) // this is NOT directory
			MatchedPaths.push_back(BasePath + FindData.cFileName);
		while( ::FindNextFile(hSearch, &FindData) == TRUE )
		{
			if( (FindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0 )
				MatchedPaths.push_back(BasePath + FindData.cFileName);
		}
		::FindClose(hSearch);
	}
}